package com.project.util;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

/**
 * java bean 拷贝工具类
 * @author chen.liangzhen
 * @time 2017年3月24日 下午3:33:36
 */
public class BeanCopyUtil extends BeanUtilsBean{
	
	private static final Logger log=LoggerFactory.getLogger(BeanCopyUtil.class);
	
	/**
	 * BeanUtils.copyProperties忽略null值 
	 * @author chen.liangzhen
	 * @time 2017年3月24日 下午3:32:41
	 * @version
	 * @param dest	目标对象
	 * @param orig	源对象
	 * @param ignoreNullFlag	是否忽略null值
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	 public void copyProperties(Object dest, Object orig, boolean ignoreNullFlag)
	       throws IllegalAccessException, InvocationTargetException {
	
	       // Validate existence of the specified beans
	       if (dest == null) {
	           throw new IllegalArgumentException
	                   ("No destination bean specified");
	       }
	       if (orig == null) {
	           throw new IllegalArgumentException("No origin bean specified");
	       }
	       if (log.isDebugEnabled()) {
	           log.debug("BeanUtils.copyProperties(" + dest + ", " +
	                     orig + "," + ignoreNullFlag + ")");
	       }
	
	
	       // Copy the properties, converting as necessary
	       if (orig instanceof DynaBean) {
	           DynaProperty[] origDescriptors =
	               ((DynaBean) orig).getDynaClass().getDynaProperties();
	           for (int i = 0; i < origDescriptors.length; i++) {
	               String name = origDescriptors[i].getName();
	               // Need to check isReadable() for WrapDynaBean
	               // (see Jira issue# BEANUTILS-61)
	               if (getPropertyUtils().isReadable(orig, name) &&
	                   getPropertyUtils().isWriteable(dest, name)) {
	                   Object value = ((DynaBean) orig).get(name);
	                   if(ignoreNullFlag){
	                   if(value!=null){
	                   copyProperty(dest, name, value);
	                   }
	                   }else{
	                   copyProperty(dest, name, value);
	                   }
	               }
	           }
	       } else if (orig instanceof Map) {
	           @SuppressWarnings("unchecked")
	           // Map properties are always of type <String, Object>
	           Map<String, Object> propMap = (Map<String, Object>) orig;
	           for (Map.Entry<String, Object> entry : propMap.entrySet()) {
	               String name = entry.getKey();
	               Object value=entry.getValue();
	               if (getPropertyUtils().isWriteable(dest, name)) {
	                   if(ignoreNullFlag){
	                   if(value!=null){
	                   copyProperty(dest, name, value);
	                   }
	                   }else{
	                   copyProperty(dest, name, value);
	                   }
	               }
	           }
	       } else /* if (orig is a standard JavaBean) */ {
	           PropertyDescriptor[] origDescriptors =
	               getPropertyUtils().getPropertyDescriptors(orig);
	           for (int i = 0; i < origDescriptors.length; i++) {
	               String name = origDescriptors[i].getName();
	               if ("class".equals(name)) {
	                   continue; // No point in trying to set an object's class
	               }
	               if (getPropertyUtils().isReadable(orig, name) &&
	                   getPropertyUtils().isWriteable(dest, name)) {
	                   try {
	                       Object value =
	                           getPropertyUtils().getSimpleProperty(orig, name);
	                       if(ignoreNullFlag){
	                   if(value!=null){
	                   copyProperty(dest, name, value);
	                   }
	                   }else{
	                   copyProperty(dest, name, value);
	                   }
	                   } catch (NoSuchMethodException e) {
	                       // Should not happen
	                   }
	               }
	           }
	       }
	 }
	 
	 /**
	  * 
	  * @author chen.liangzhen
	  * @time 2017年3月24日 下午3:42:36
	  * @version
	  * @param source
	  * @param target
	  * @param editable
	  * @param ignoreProperties
	  * @throws BeansException
	  */
    /*private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)  
            throws BeansException {  
      
        Assert.notNull(source, "Source must not be null");  
        Assert.notNull(target, "Target must not be null");  
      
        Class<?> actualEditable = target.getClass();  
        if (editable != null) {  
            if (!editable.isInstance(target)) {  
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() +  
                        "] not assignable to Editable class [" + editable.getName() + "]");  
            }  
            actualEditable = editable;  
        }  
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);  
        List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;  
      
        for (PropertyDescriptor targetPd : targetPds) {  
            Method writeMethod = targetPd.getWriteMethod();  
            if (writeMethod != null && (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {  
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());  
                if (sourcePd != null) {  
                    Method readMethod = sourcePd.getReadMethod();  
                    if (readMethod != null &&  
                            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {  
                        try {  
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {  
                                readMethod.setAccessible(true);  
                            }  
                            Object value = readMethod.invoke(source);  
                            if(value != null){  //只拷贝不为null的属性 by zhao  
                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {  
                                    writeMethod.setAccessible(true);  
                                }  
                                writeMethod.invoke(target, value);  
                            }  
                        }  
                        catch (Throwable ex) {  
                            throw new FatalBeanException(  
                                    "Could not copy property '" + targetPd.getName() + "' from source to target", ex);  
                        }  
                    }  
                }  
            }  
        }  
    }*/
	
	/**
	 * 
	 * @author chen.liangzhen
	 * @time 2017年3月24日 下午3:45:48
	 * @version v0.1.0
	 * @param source
	 * @return
	 */
	public static String[] getNullPropertyNames (Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<String>();
        for(java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) emptyNames.add(pd.getName());
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

	/**
	 * 
	 * @author chen.liangzhen
	 * @time 2017年3月24日 下午3:45:57
	 * @version v0.1.0
	 * @param src
	 * @param target
	 */
    public static void copyPropertiesIgnoreNull(Object src, Object target){
        BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
    }
    
    /**
     * main
     * @author chen.liangzhen
     * @time 2017年3月24日 下午3:47:30
     * @version
     * @param args
     */
    public static void main(String[] args) {
    	/*UserVo src = new UserVo();
    	src.setCode("chenlz");
    	src.setName("真真");
    	src.setCreateDate(new Date());
    	
    	UserDto target = new UserDto();
    	
    	copyPropertiesIgnoreNull(src, target);
    	System.out.println(target.toString());*/
	}
}